﻿using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using Microsoft.Practices.Unity;

namespace SpectrumAnalyser.Services.Common.Bevaviors
{
    public class UnityServiceBehavior : IServiceBehavior
    {
        private readonly IUnityContainer container;

        public UnityServiceBehavior(IUnityContainer container)
        {
            this.container = container;
        }

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
        }

        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase,
                                         Collection<ServiceEndpoint> endpoints,
                                         BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
            {
                foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
                {
                    if (endpointDispatcher.ContractName != "IMetadataExchange" &&
                        endpointDispatcher.ContractName != "IHttpGetHelpPageAndMetadataContract")
                    {
                        string contractName = endpointDispatcher.ContractName;
                        ServiceEndpoint serviceEndpoint =
                            serviceDescription.Endpoints.FirstOrDefault(e => e.Contract.Name == contractName);
                        endpointDispatcher.DispatchRuntime.InstanceProvider = new UnityInstanceProvider(this.container,
                                                                                                        serviceEndpoint
                                                                                                            .Contract
                                                                                                            .ContractType);
                    }
                }
            }
        }

        internal class UnityInstanceProvider : IInstanceProvider
        {
            private readonly IUnityContainer container;
            private readonly Type contractType;

            public UnityInstanceProvider(IUnityContainer container, Type contractType)
            {
                this.container = container;
                this.contractType = contractType;
            }

            public object GetInstance(InstanceContext instanceContext)
            {
                return GetInstance(instanceContext, null);
            }

            public object GetInstance(InstanceContext instanceContext, Message message)
            {
                return container.Resolve(contractType);
            }

            public void ReleaseInstance(InstanceContext instanceContext, object instance)
            {
                container.Teardown(instance);
            }
        }
    }
}